/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ola_pal_os.h"

#include <stdlib.h>
#include <stddef.h>
#include <stdarg.h>
#include "cmsis_os2.h"
#include "securec.h"

#define OsLog(...) \
    do { \
        printf("[OLA_PAL_OS][%s][%d]", __func__, __LINE__); \
        printf(__VA_ARGS__); \
        printf("\r\n"); \
    } while (0)
#define MS_PER_SEC  1000

OlaTask OlaTaskCreate(OlaTaskFunc func, void *param, const OlaTaskParam *taskParam)
{
    if ((func == NULL) || (taskParam == NULL) || (taskParam->taskName == NULL)) {
        OsLog("invalid param");
        return NULL;
    }

    osThreadAttr_t attr;
    (void)memset_s(&attr, sizeof(osThreadAttr_t), 0, sizeof(osThreadAttr_t));
    attr.name = taskParam->taskName;
    attr.priority = taskParam->level;
    attr.stack_size = taskParam->stackSize;

    osThreadId_t threadId = osThreadNew((osThreadFunc_t)func, param, &attr);
    if (threadId == NULL) {
        OsLog("create task fail");
        return NULL;
    }

    return threadId;
}

int OlaTaskDelete(OlaTask handle)
{
    if (handle == NULL) {
        OsLog("invalid param");
        return -1;
    }
    if (osThreadTerminate((osThreadId_t)handle) != osOK) {
        OsLog("delete error");
        return -1;
    }
    return 0;
}

OlaTask OlaTaskGetCurTaskId(void)
{
    return (OlaTask)osThreadGetId();
}

int OlaTaskSuspend(OlaTask handle)
{
    if (handle == NULL) {
        OsLog("invalid param");
        return -1;
    }
    if (osThreadSuspend((osThreadId_t)handle) != osOK) {
        OsLog("suspend error");
        return -1;
    }
    return 0;
}

int OlaTaskResume(OlaTask handle)
{
    if (handle == NULL) {
        OsLog("invalid param");
        return -1;
    }
    if (osThreadResume((osThreadId_t)handle) != osOK) {
        OsLog("resume error");
        return -1;
    }
    return 0;
}

OlaTaskStuts OlaTaskGetState(OlaTask handle)
{
    if (handle == NULL) {
        OsLog("invalid param");
        return -1;
    }
    osThreadState_t state = osThreadGetState((osThreadId_t)handle);
    switch (state) {
        case osThreadReady:
            return OLA_TASK_READY;
        case osThreadRunning:
            return OLA_TASK_RUNNING;
        case osThreadBlocked:
            return OLA_TASK_BLOCKED;
        case osThreadTerminated:
            return OLA_TASK_TERMINATED;
        case osThreadInactive:
            break;
        case osThreadError:
            break;
        case osThreadReserved:
            break;
        default:
            break;
    }

    return OLA_TASK_ERROR;
}

OlaMutex OlaMutexCreate(void)
{
    return (OlaMutex)osMutexNew(NULL);
}

int OlaMutexDestroy(OlaMutex mutex)
{
    if (mutex == NULL) {
        OsLog("invalid param");
        return -1;
    }

    if (osMutexDelete((osMutexId_t)mutex) != osOK) {
        OsLog("destroy mutex fail");
        return -1;
    }
    return 0;
}

int OlaMutexLock(OlaMutex mutex, unsigned int timeout)
{
    if (mutex == NULL) {
        OsLog("invalid param");
        return -1;
    }
    if (osMutexAcquire((osMutexId_t)mutex, timeout) != osOK) {
        OsLog("acquire mutex fail");
        return -1;
    }
    return 0;
}

int OlaMutexUnLock(OlaMutex mutex)
{
    if (mutex == NULL) {
        OsLog("invalid param");
        return -1;
    }
    if (osMutexRelease((osMutexId_t)mutex) != osOK) {
        OsLog("release mutex fail");
        return -1;
    }
    return 0;
}

OlaSem OlaSemCreate(unsigned int initCount)
{
    return osSemaphoreNew(0, initCount, NULL);
}

int OlaSemDestroy(OlaSem handle)
{
    if (handle == NULL) {
        OsLog("invalid param");
        return -1;
    }
    if (osSemaphoreDelete((osSemaphoreId_t)handle) != osOK) {
        OsLog("destroy sem fail");
        return -1;
    }
    return 0;
}

int OlaSemPost(OlaSem handle)
{
    if (handle == NULL) {
        OsLog("invalid param");
        return -1;
    }
    if (osSemaphoreRelease((osSemaphoreId_t)handle) != osOK) {
        OsLog("release sem fail");
        return -1;
    }
    return 0;
}

static unsigned int MsToTick(unsigned int ms)
{
    unsigned long long tick = (unsigned long long)ms * osKernelGetTickFreq() / MS_PER_SEC;
    return (unsigned int)tick;
}

int OlaSemAcquire(OlaSem handle, unsigned int timeout)
{
    if (handle == NULL) {
        OsLog("invalid param");
        return -1;
    }
    if (osSemaphoreAcquire((osSemaphoreId_t)handle, MsToTick(timeout)) != osOK) {
        OsLog("release sem fail");
        return -1;
    }
    return 0;
}

void OlaSleepMs(unsigned int ms)
{
    if (osDelay(MsToTick(ms)) != osOK) {
        OsLog("sleep fail");
        return;
    }
    return;
}

static unsigned long long TickToMs(unsigned int tick)
{
    return (unsigned long long)tick * MS_PER_SEC / osKernelGetTickFreq();
}

int OlaGetTime(unsigned long long *ms)
{
    if (ms == NULL) {
        OsLog("invalid param");
        return -1;
    }
    *ms = TickToMs(osKernelGetTickCount());

    return 0;
}

int OlaPrintf(const char *format, ...)
{
    if (format == NULL) {
        return 0;
    }
    va_list ap;
    va_start(ap, format);
    int ret = vprintf(format, ap);
    va_end(ap);
    return ret;
}

void *OlaMalloc(unsigned int size)
{
    if (size == 0) {
        return NULL;
    }
    return malloc(size);
}

void OlaFree(void *ptr)
{
    if (ptr != NULL) {
        free(ptr);
    }
    return;
}
